<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <title>WebSocket 语音助手 Demo</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    button { margin: 5px; padding: 8px 16px; }
    #log { margin-top: 20px; white-space: pre-wrap; background: #f4f4f4; padding: 10px; border: 1px solid #ccc; height: 200px; overflow-y: auto; }
  </style>
</head>
<body>
  <h2>WebSocket 语音助手 Demo</h2>

  <div>
    <button onclick="connectWS()">连接服务器</button>
    <button id="recordBtn" onclick="startRecording()">🎤 开始录音</button>
    <button id="stopBtn" onclick="stopRecording()" style="display:none">⏹️ 停止录音</button>
    <button onclick="sendText()">📝 发送文字</button>
    <button onclick="sendInterrupt()">⚡ 中断</button>
  </div>

  <div style="margin-top:10px;">
    <input id="textInput" placeholder="输入文字消息" style="width:200px;">
  </div>

  <div id="log"></div>

<script>
let ws;
let isConnected = false;
let isRecording = false;
let inputAudioContext;
let mediaRecorder;

function log(msg) {
  console.log(msg);
  const logDiv = document.getElementById('log');
  logDiv.textContent += msg + "\n";
  logDiv.scrollTop = logDiv.scrollHeight;
}

// 建立 WebSocket 连接
function connectWS() {
  ws = new WebSocket("ws://localhost:8765");
  ws.binaryType = "arraybuffer";

  ws.onopen = () => {
    isConnected = true;
    log("✅ 已连接服务器");
    ws.send(JSON.stringify({ type: "session", id: "123", token: "jwt-token" }));
  };

  ws.onmessage = (evt) => {
    if (typeof evt.data === "string") {
      log("📩 收到消息: " + evt.data);
    } else {
      log("🎶 收到二进制数据 (长度: " + evt.data.byteLength + ")");
      // 这里可以用 AudioContext 播放 PCM
    }
  };

  ws.onclose = () => {
    isConnected = false;
    log("❌ 连接已关闭");
  };
  ws.onerror = (err) => log("⚠️ WebSocket 错误: " + err);
}

function sendMessage(obj) {
  if (ws && ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify(obj));
    log("➡️ 发送: " + JSON.stringify(obj));
  }
}

// 开始录音
async function startRecording() {
  if (!isConnected) {
    alert("请先连接到服务器");
    return;
  }

  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      audio: {
        sampleRate: 16000,
        channelCount: 1,
        echoCancellation: true,
        noiseSuppression: true
      }
    });

    inputAudioContext = new AudioContext({ sampleRate: 16000 });
    const source = inputAudioContext.createMediaStreamSource(stream);
    const scriptProcessor = inputAudioContext.createScriptProcessor(4096, 1, 1);

    let audioDataCount = 0;
    scriptProcessor.onaudioprocess = function (event) {
      if (!isRecording) return;

      const inputData = event.inputBuffer.getChannelData(0);
      const pcm16 = new Int16Array(inputData.length);
      for (let i = 0; i < inputData.length; i++) {
        pcm16[i] = Math.max(-32768, Math.min(32767, inputData[i] * 32767));
      }

      if (ws && ws.readyState === WebSocket.OPEN) {
        ws.send(pcm16.buffer);
        audioDataCount++;
        if (audioDataCount % 100 === 0) {
          log(`🎤 已发送 ${audioDataCount} 包音频 (${pcm16.length} 样本)`);
        }
      }
    };

    source.connect(scriptProcessor);
    scriptProcessor.connect(inputAudioContext.destination);

    isRecording = true;
    document.getElementById("recordBtn").style.display = "none";
    document.getElementById("stopBtn").style.display = "inline-flex";

    sendMessage({ type: "audio", data: "start" });
    mediaRecorder = { stream, scriptProcessor };
    log("🎤 实时录音已开始");

  } catch (err) {
    console.error(err);
    alert("录音失败: " + err.message);
  }
}

// 停止录音
function stopRecording() {
  if (!isRecording) return;
  isRecording = false;

  if (mediaRecorder) {
    mediaRecorder.stream.getTracks().forEach(track => track.stop());
    mediaRecorder.scriptProcessor.disconnect();
  }
  if (inputAudioContext) inputAudioContext.close();

  sendMessage({ type: "audio", data: "end" });
  document.getElementById("recordBtn").style.display = "inline-flex";
  document.getElementById("stopBtn").style.display = "none";
  log("⏹️ 已停止录音");
}

// 发送文字消息
function sendText() {
  const text = document.getElementById("textInput").value;
  if (!text) return;
  sendMessage({ type: "text-input", text });
}

// 发送中断信号
function sendInterrupt() {
  sendMessage({ type: "interrupt-signal" });
  log("⚡ 已发送中断信号");
}
</script>
</body>
</html>
